
#include <cstdio>
#include <BFile>
#include <BFolder>
#include <BBuffer>
#include <BLibrary>
#include <BReadWrite>
#include "IGlobal.h"

using namespace BWE;

bool RenameExtAlias(BString& ext)
{
	auto it = app_extAlias.find(ext);
	if (it.valid())
	{
		ext = it.value();
		return true;
	}
	return false;
}

BWEAPI bool BWE::SetReadWrite(const BString& ext, BReadWrite* rw)
{
	for (auto it = app_plugins.begin(); it != app_plugins.end(); ++it)
	{
		bplugin& plugin = *it;
		if (plugin.ext == ext)
		{
			plugin.rw = rw;
			return true;
		}
	}
	bplugin& plugin = app_plugins.append();
	plugin.ext = ext;
	plugin.rw = rw;
	return true;
}
BWEAPI BReadWrite* BWE::GetReadWrite(const BString& extension)
{
	if (extension.empty())
		return 0;

	BString ext = extension;
	RenameExtAlias(ext);

	for (auto it = app_plugins.begin(); it != app_plugins.end(); ++it)
	{
		bplugin& plugin = *it;
		if (plugin.ext == ext)
		{
			return plugin.rw;
		}
	}

	BString pluginName("brw-");
	pluginName += ext;
#ifdef BWE_DEBUG
	pluginName += 'd';
#endif

#ifdef linux
	pluginName += ".so";
#endif

#ifdef _WIN32
	pluginName += ".dll";
#endif

	BString pluginPath;
	for (int i = 0; i < app_pluginPaths.size(); i++)
	{
		BString fileName = app_pluginPaths[i];
		fileName << '/' << pluginName;
		if (BFile::Exist(fileName))
		{
			pluginPath = fileName;
			break;
		}
	}

	if (pluginPath.empty())
	{
		BString fileName("./plugin/");
		fileName << pluginName;
		if (BFile::Exist(fileName))
			pluginPath = fileName;
	}

	if (pluginPath.empty())
	{
		BString fileName = app_libPath;
		fileName << "/plugin/" << pluginName;
		if (BFile::Exist(fileName))
			pluginPath = fileName;
	}

	if (pluginPath.empty())
	{
		return 0;
	}

	BLibraryHolder lib = new BLibrary(pluginPath);
	if (!lib->load())
	{
		printf("Error : Can't load plugin %s.\n", pluginPath.cstr());
		return 0;
	}

	func_get get = (func_get)lib->get("get");
	if (!get)
	{
		printf("Error : Can't find interface [get] in plugin %s.\n", pluginPath.cstr());
		return 0;
	}

	BReadWrite* rw = get();
	if (rw == 0)
		return 0;

	bplugin plugin;
	plugin.rw = rw;
	plugin.lib = lib;
	plugin.ext = ext;
	app_plugins.append(plugin);
	return rw;
}

BWEAPI bool BWE::SetExtAlias(const BString& ext, const BString& alias)
{
	if (ext.empty())
		return false;
	if (alias.empty())
	{
		return app_extAlias.remove(ext);
	}
	app_extAlias[ext] = alias;
	return true;
}
BWEAPI bool BWE::GetExtAlias(const BString& ext, BString& alias)
{
	if (ext.empty())
		return false;
	auto it = app_extAlias.find(ext);
	if (it.valid())
	{
		alias = it.value();
		return true;
	}
	return false;
}

BWEAPI BObject* BWE::ReadFile(const BString& fileName)
{
	BString ext = fileName.ext();
	if (ext.empty())
	{
		printf("ReadFile Error : can't find plugin without extension.\n");
		return 0;
	}
	ext.lower();
	RenameExtAlias(ext);
	if (BReadWrite* rw = GetReadWrite(ext))
	{
		auto it = app_resources.find(fileName);
		if (it.valid())
		{
			BBuffer& buffer = *it;
			buffer.seek(0);
			if (BObject* object = rw->read(buffer))
				return object;
		}
		else if (BFile::Exist(fileName))
		{
			if (BObject* object = rw->load(fileName))
				return object;
		}
		const BString& message = rw->message();
		if (message.size())
			printf("ReadFile Error : %s.\n", message.cstr());
		return 0;
	}
	printf("ReadFile Error : can't find plugin for %s.\n", ext.cstr());
	return 0;
}
BWEAPI bool BWE::ReadFile(const BString& fileName, BObject* object)
{
	BString ext = fileName.ext();
	if (ext.empty())
	{
		printf("ReadFile Error : can't find plugin without extension.\n");
		return 0;
	}
	ext.lower();
	RenameExtAlias(ext);
	if (BReadWrite* rw = GetReadWrite(ext))
	{
		auto it = app_resources.find(fileName);
		if (it.valid())
		{
			BBuffer& buffer = *it;
			buffer.seek(0);
			if (rw->read(buffer, object))
				return true;
		}
		else if (BFile::Exist(fileName))
		{
			if (rw->load(fileName, object))
				return true;
		}
		const BString& message = rw->message();
		if (message.size())
			printf("ReadFile Error : %s.\n", message.cstr());
		return false;
	}
	printf("ReadFile Error : can't find plugin for %s.\n", ext.cstr());
	return false;
}
BWEAPI bool BWE::WriteFile(const BString& fileName, const BObject* object)
{
	BString ext = fileName.ext();
	if (ext.empty())
	{
		printf("WriteFile Error : can't find plugin without extension.\n");
		return 0;
	}
	ext.lower();
	RenameExtAlias(ext);
	if (BReadWrite* rw = GetReadWrite(ext))
	{
		if (rw->save(fileName, object))
			return true;
		const BString& message = rw->message();
		printf("WriteFile Error : error message : %s.\n", message.cstr());
		return false;
	}
	printf("WriteFile Error : can't find plugin for %s.\n", ext.cstr());
	return false;
}

BWEAPI bool BWE::RegistResource(const char* info, const BByte* data)
{
	if (!info || !data)
		return false;
	int offset = 0;
	int begin = 0;
	int volpos = 0;
	int pos = 0;
	BString name;
	BString volumn;
	while (info[pos])
	{
		if (info[pos] == '(')
		{
			volpos = pos++;
			continue;
		}
		if (info[pos] == ';')
		{
			name.reset();
			name.append(info, begin, volpos);
			name.trimmed();
			volumn.reset();
			volumn.append(info, volpos, pos);
			volumn.trimmed();
			volumn.remove('(');
			volumn -= ')';
			int size = volumn.toInt();
			IResource& resource = *app_resources.insert(name);
			resource.reset(data + offset, size);
			begin = pos + 1;
			volpos = pos + 1;
			offset += size;
		}
		pos++;
	}
	return true;
}
BWEAPI bool BWE::UnregistResource(const char* info)
{
	if (!info)
		return false;
	int offset = 0;
	int begin = 0;
	int volpos = 0;
	int pos = 0;
	BString name;
	BString volumn;
	while (info[pos])
	{
		if (info[pos] == '(')
		{
			volpos = pos++;
			continue;
		}
		if (info[pos] == ';')
		{
			name.reset();
			name.append(info, begin, volpos);
			name.trimmed();
			volumn.reset();
			volumn.append(info, volpos, pos);
			volumn.trimmed();
			volumn.remove('(');
			volumn -= ')';
			int size = volumn.toInt();
			app_resources.remove(name);
			begin = pos + 1;
			volpos = pos + 1;
			offset += size;
		}
		pos++;
	}
	return true;
}

BWEAPI bool BWE::ReadText(const char* fileName, BString& text)
{
	auto it = app_resources.find(fileName);
	if (it.valid())
	{
		IResource& resource = it.value();
		resource.seek(0);
		return resource.read(text);
	}
	return BFile::Load(fileName, text);
}
BWEAPI bool BWE::ReadText(const BString& fileName, BString& text)
{
	auto it = app_resources.find(fileName);
	if (it.valid())
	{
		IResource& resource = it.value();
		resource.seek(0);
		return resource.read(text);
	}
	return BFile::Load(fileName, text);
}

BWEAPI int BWE::GetResourceCount()
{
	return app_resources.size();
}
BWEAPI const BString& BWE::GetResourceName(int index)
{
	auto it = app_resources.seek(index);
	return it.key();
}

BWEAPI const BBuffer& BWE::GetResource(int index)
{
	auto it = app_resources.seek(index);
	return it.value();
}
BWEAPI const BBuffer& BWE::GetResource(const char* name)
{
	auto it = app_resources.find(name);
	return it.value();
}
BWEAPI const BBuffer& BWE::GetResource(const BString& name)
{
	auto it = app_resources.find(name);
	return it.value();
}

